using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BencodeNET.Torrents;
using Jackett.Common.Extensions;
using Jackett.Common.Models;
using Jackett.Common.Models.IndexerConfig;
using Jackett.Common.Services.Interfaces;
using Jackett.Common.Utils;
using Jackett.Common.Utils.Clients;
using Newtonsoft.Json.Linq;
using NLog;

namespace Jackett.Common.Indexers
{
    [ExcludeFromCodeCoverage]
    public class HuginnRss : IndexerBase
    {
        public override string Id => "huginnrss";
        public override string Name => "HuginnRss";
        public override string Description => "HuginnRss";
        public override string SiteLink { get; protected set; } = "http://10.107.1.247:3000/";

        private string RssPath => ((ConfigurationData.StringConfigurationItem)base.configData.GetDynamic("RssPath"))?.Value ?? "users/1/web_requests/41/a-secret-key.json";
        public override string Language => "zh-CN";
        public override string Type => "public";

        public override TorznabCapabilities TorznabCaps => SetCapabilities();

        private string SearchEndpoint => SiteLink + RssPath;

        private new ConfigurationData configData => base.configData;

        public HuginnRss(IIndexerConfigurationService configService, WebClient wc, Logger l, IProtectionService ps, ICacheService cs)
            : base(configService: configService,
                   client: wc,
                   logger: l,
                   p: ps,
                   cacheService: cs,
                   configData: new ConfigurationData())
        {
            base.configData.AddDynamic("RssPath", new ConfigurationData.StringConfigurationItem("RssPath"));

        }

        private TorznabCapabilities SetCapabilities()
        {
            var caps = new TorznabCapabilities
            {
                TvSearchParams = new List<TvSearchParam>
                {
                    TvSearchParam.Q, TvSearchParam.Season, TvSearchParam.Ep
                },
                MovieSearchParams = new List<MovieSearchParam>
                {
                    MovieSearchParam.Q
                }
            };

            // torrents.csv doesn't return categories
            caps.Categories.AddCategoryMapping(1, TorznabCatType.Movies);
            //caps.Categories.AddCategoryMapping(1, TorznabCatType.Other);

            return caps;
        }


        public override async Task<IndexerConfigurationStatus> ApplyConfiguration(JToken configJson)
        {
            LoadValuesFromJson(configJson);
            var releases = await PerformQuery(new TorznabQuery());

            await ConfigureIfOK(string.Empty, releases.Any(),
                                () => throw new Exception("Error: 0 results found!"));

            return IndexerConfigurationStatus.Completed;
        }

        protected override async Task<IEnumerable<ReleaseInfo>> PerformQuery(TorznabQuery query)
        {
            var releases = new List<ReleaseInfo>();
            var searchString = query.GetQueryString();

            // not supported
            if (searchString.IsNullOrWhiteSpace())
            {
                releases.Add(new ReleaseInfo
                {
                    Title = "[NOT IMPLEMENTED] Empty search is unsupported in this indexer",
                    Guid = new Uri(SiteLink),
                    Details = new Uri(SiteLink),
                    MagnetUri = new Uri("magnet:?xt=urn:btih:3333333333333333333333333333333333333333"), // unknown torrent
                    Category = new List<int> { TorznabCatType.Movies.ID },
                    PublishDate = new DateTime(),
                    Grabs = 0,
                    Seeders = 0,
                    Peers = 0,
                    DownloadVolumeFactor = 0,
                    UploadVolumeFactor = 1
                });

                return releases;
            }

            // search needs at least 3 characters
            if (searchString.IsNotNullOrWhiteSpace() && searchString.Length < 3)
            {
                return releases;
            }



            var searchUrl = SearchEndpoint;
            var response = await RequestWithCookiesAndRetryAsync(searchUrl);

            try
            {
                var jsonContent = (JArray)JObject.Parse(response.ContentString)["items"];
                //var yearMatch = Regex.Match(searchString, "\\d{4}$");
                //string year = "";
                //if (yearMatch.Success)
                //{
                //    year = yearMatch.Value;
                //    searchString= searchString.Substring(0, searchString.Length-4);
                //}
                string[] searchWords = searchString.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
                foreach (var torrent in jsonContent)
                {
                    if (torrent == null)
                    {
                        throw new Exception("Error: No data returned!");
                    }
                    var title = torrent.Value<string>("title");

                    //if (title.IsNullOrWhiteSpace() || searchWords.Any(w => !title.Contains(w)))
                    //{
                    //    continue;
                    //}
                    var alias = torrent.Value<string>("alias");
                    if (alias.IsNotNullOrWhiteSpace())
                    {
                        if (searchWords.Any(w => !alias.Contains(w)))
                        {
                            continue;
                        }
                    }
                    else
                    {
                        continue;
                    }
                    var link = torrent.Value<string>("link");
                    var infoHash = torrent.Value<string>("hash");
                    if (link.IsNotNullOrWhiteSpace() && infoHash.IsNullOrWhiteSpace())
                    {
                        var match = Regex.Match(link, "(?i)magnet:(\\?xt=urn:btih:)?(?<value>[A-Z0-9]{40})");
                        if (match.Success)
                        {
                            infoHash = match.Groups["value"].Value;
                        }
                    }
                    if (infoHash.IsNullOrWhiteSpace())
                    { continue; }

                    var size = torrent.Value<long>("size");
                    var seeders = torrent.Value<int?>("seeders") ?? 1;
                    var leechers = torrent.Value<int?>("leechers") ?? 1;
                    var grabs = torrent.Value<int?>("completed") ?? 0;
                    var publishDate = DateTimeUtil.FromUnknown(torrent.Value<string>("pubDate"));
                    var pageUrl = torrent.Value<string>("pageUrl") ?? link;
                    var release = new ReleaseInfo
                    {
                        Title = title,
                        Details = new Uri(pageUrl), // there is no details link
                        Guid = new Uri($"magnet:?xt=urn:btih:{infoHash}"),
                        InfoHash = infoHash, // magnet link is auto generated from infohash
                        Category = new List<int> { TorznabCatType.Movies.ID },
                        PublishDate = publishDate,
                        Size = size,
                        Grabs = grabs,
                        Seeders = seeders,
                        Peers = leechers + seeders,
                        DownloadVolumeFactor = 0,
                        UploadVolumeFactor = 1
                    };

                    releases.Add(release);
                }
            }
            catch (Exception ex)
            {
                OnParseError(response.ContentString, ex);
            }

            return releases
                   .OrderByDescending(o => o.PublishDate)
                   .ToArray();
        }
    }
}
